
#pragma once

#include <afxwin.h>
#include "Math/Math3d.h"
#include "HrkNode.h"
#include <vector>
#include "tinyxml/tinyxml.h"

#define TIME_EPSILON 1e-5

typedef enum
{
	eInterp_UNKOWN,
	eInterp_LINEAR, 
	eInterp_SPLINE
	
}InterpMode;//interpolate mode

template <typename T>
struct keyDef_t
{
public:
	int frame_index;
	T val;
};

struct floatKeyDef_t:public keyDef_t<float>
{
};

struct quatKeyDef_t:public keyDef_t<GLquat>
{
};

class CHrkNodeAnim;

template <typename T>
class CHrkKeyTab: public std::vector<keyDef_t<T>>
{
public:

	CHrkNodeAnim* m_pGroupAnim;
	InterpMode m_interpMode;
	int m_keyCount;	

	T m_valInterp;
	
	int FindClosestKeyIndex( int frame,float t );
	
	CHrkKeyTab()
	{
		m_keyCount = 0;
		m_interpMode = eInterp_UNKOWN;

	}

	virtual void InterpolateKeyframe( int keyIndex,int frame,int t ) = 0;
	virtual bool  IsLoop(int frameCount) = 0;
	void ReverseKeyData(){
		int nKeys = size();
		int i = 0;
		int j = nKeys-1;
		while( i<j){
			T t = _Myfirst[i].val;
			_Myfirst[i].val = _Myfirst[j].val;
			_Myfirst[j].val = t;
			i++;j--;
		}
	}

	bool HaveAnimKeys();
};

class CHrkQuatKeyTab: public CHrkKeyTab<GLquat>
{
public:
	virtual void InterpolateKeyframe( int keyIndex,int frame,int t );
	virtual bool  IsLoop(int frameCount);
};

class CHrkFloatKeyTab: public CHrkKeyTab<float>
{
public:
	virtual void InterpolateKeyframe( int keyIndex,int frame,int t );
	virtual bool  IsLoop(int frameCount);
};

class CHrkAnim;

class CHrkNodeAnim
{
public:

	CHrkNodeAnim(CHrkAnim* pAnim)
		:m_pAnim(pAnim)
	{
		
	}


	CHrkAnim* m_pAnim;
	CHrkNode* m_pNode;
	
	CHrkFloatKeyTab m_posKeys[3];//x,y,z
	CHrkQuatKeyTab m_rotKeysQ;

	void LoadNodeAnim( const TiXmlElement* elem );
	void UpdateNodeAnim(int frame,float t, const GLvec3f &BipRotInv);
	bool HavePosAnim();
	bool HaveRotAnim();

	bool HaveAnim();

	void ReverseKeyData()
	{
		m_posKeys[0].ReverseKeyData();
		m_posKeys[1].ReverseKeyData();
		m_posKeys[2].ReverseKeyData();
		m_rotKeysQ.ReverseKeyData();
	}
};

class CHrkAnim
{
public:

	CHrkNode* m_nodeRoot;
	float yaw_add;

	std::vector< std::string > m_topName;
	bool m_bBiped;

	int m_nNodeCount;
	int m_nFrameCount;
	int m_nFrameRate;
	float m_fFrameTick;

	bool	m_bMoveY;
	bool	m_bLoop;

	GLvec3f		m_BipRotInv;	//"Bip01" relative rotation referenced to "Ground" 

	CHrkAnim()
	{
		yaw_add = 0.f;
	}

	virtual ~CHrkAnim();
	void Destroy();

	bool LoadFromFile( const char* filename );
	CHrkNode* FindNode(const char* nodeName, CHrkNode *pNode);

	void ReverseAnimData()
	{
		int n = m_nodeAnims.size();
		for( int i = 0;i<n;i++ ){
			m_nodeAnims[i]->ReverseKeyData();
		}
	}

	void UpdateAnim(float t);
	float FindClosestFrameIndex( float t,int& k, float frame_tick = 0.0f );
	float GetTimeLength(){return m_nFrameCount*m_fFrameTick;}

	bool CheckLoop();

	int GetFrameCount() { return m_nFrameCount; }

	float GetFrameTick() { return m_fFrameTick; }
	int GetFrameRate()	{ return m_nFrameRate; }

	void SetMoveY(bool flag) { m_bMoveY = flag; }
	bool GetMoveY() { return m_bMoveY; }

	void SetLoop(bool flag) { m_bLoop = flag; }
	bool GetLoop() { return m_bLoop; }

	GLvec3f GetBipRotInv()  { return m_BipRotInv; }
	
	void SetBipRotInv(int axis, float rot) {
		if (axis == 0) {
			m_BipRotInv.x = rot;
		}
		if (axis == 1) {
			m_BipRotInv.y = rot;
		}
		if (axis == 2) {
			m_BipRotInv.z = rot;
		}
	};

	void SetBipedAnimation( const char* name );

	CHrkNodeAnim* FindNodeAnim( const char* name );

	std::vector<CHrkNodeAnim*> m_nodeAnims;

	std::string m_name;
};
